<?php

namespace App\Services;

use App\Models\InsideMessage;
use App\Repository\Contracts\InsideMessageContentRepository;
use App\Repository\Contracts\InsideMessageRepository;
use App\Repository\Contracts\InsideMessageSystemRepository;
use App\Repository\Contracts\UserRepository;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Validation\UnauthorizedException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;

/**
 * 站内信息服务
 *
 * Class InsideMessageService
 * @package App\Services
 */
class InsideMessageService
{
    /**
     * 用户和管理员模型
     */
    public const MODEL_NAME = [
        'USER' => 'App\Models\User',
        'ADMIN'=> 'App\Models\Admin',
    ];

    /**
     * 信息状态
     */
    public const MESSAGE_STATE = [
        'UNREAD' => '0',
        'READ'   => '1',
        'CANCEL' => '2',
    ];

    public const SINGLE_TYPE = '2';

    public const SYSTEM_TYPE = '1';

    /**
     * 信息类型
     */
    public const MESSAGE_TYPE = [
        'SINGLE' => self::SINGLE_TYPE,
        'SYSTEM' => self::SYSTEM_TYPE,
    ];

    /**
     * 批量操作个数
     */
    public const BATCH_NUM = 500;

    protected $insideMessageRepository;

    protected $insideMessageContentRepository;

    protected $insideMessageSystemRepository;

    protected $userRepository;

    public function __construct(
        InsideMessageRepository $insideMessageRepository,
        InsideMessageContentRepository $insideMessageContentRepository,
        InsideMessageSystemRepository $insideMessageSystemRepository,
        UserRepository $userRepository
    )
    {
        $this->insideMessageRepository = $insideMessageRepository;
        $this->insideMessageContentRepository = $insideMessageContentRepository;
        $this->insideMessageSystemRepository = $insideMessageSystemRepository;
        $this->userRepository = $userRepository;
    }

    /**
     * 根据传入的参数，获取类型值
     *
     * @param $type
     * @return mixed|null
     */
    public function getRealMessageType($type)
    {
        $type = static::MESSAGE_TYPE[strtoupper($type)] ?? null;

        throw_if(
            !$type,
            new NotFoundHttpException()
        );

        return $type;
    }

    /**
     * 根据类型，获取站内信分页列表
     *
     * @param $type
     * @return LengthAwarePaginator
     */
    public function getMessageListByType($type): LengthAwarePaginator
    {
        $currentUser = optional(request())->user();

        throw_if(
            !$currentUser || 0 !== strcmp(static::MODEL_NAME['USER'], get_class($currentUser)),
             new UnauthorizedException()
        );

        // 系统站内通知
        if (0 === strcmp($type, static::SYSTEM_TYPE)) {
            return $this->insideMessageSystemRepository->findMessagesByUserId($currentUser->id);
        }

        // 点对点通知
        return $this->insideMessageRepository->findMessagesByUserId($currentUser->id);
    }

    /**
     * 获取站内详细信息
     *
     * @param $id
     * @return mixed
     */
    public function getMessageById($id)
    {
        return $this->insideMessageRepository->findMessageDetailInfoByContentId($id);
    }

    /**
     * 点对点内部消息发送
     *
     * @param array $data
     * @return mixed
     */
    public function sendPointToPointMessage(array $data)
    {
        return $this->saveBaseInsideMessageInfo($data, static::MESSAGE_TYPE['SINGLE']);
    }

    /**
     * 系统信息推送批量操作
     *
     * @param array $data
     * @return bool
     */
    public function sendSystemInfoByCondition(array $data): bool
    {
        $condition = json_decode($data['condition'], true);
        $message = $this->saveBaseInsideMessageInfo($data, static::MESSAGE_TYPE['SYSTEM']);

        if (empty($message) || !$message instanceof InsideMessage) {
            return false;
        }

        $ids = $this->userRepository->findSystemMessageUserIds($condition, static::BATCH_NUM);

        if (empty($ids)) {
            return false;
        }

        $result = $this->createSystemMessageInfoForBatch($ids,  $message->id, static::BATCH_NUM);

        return !empty($result) && count($result) >= 1;
    }

    /**
     * 标记已读
     *
     * @param $messageId
     * @return bool
     */
    public function readMessageById($messageId): bool
    {
        $message = $this->insideMessageRepository->find($messageId);
        $currentUser = optional(request())->user();

        if (empty($currentUser) || empty($message)) {
            return false;
        }

        // 如果是管理员查看，则不修改状态。
        if (0=== strcmp(static::MODEL_NAME['ADMIN'], get_class($currentUser))) {
            return true;
        }

        $readState = $this->getCancelPointData('READ');


        if (0 === strcmp($message->type, static::MESSAGE_TYPE['SINGLE'])) {
            return $this->readSingleMessage($message, $currentUser, $readState);
        }

        return $this->readSystemMessage($message, $currentUser, $readState);
    }

    /**
     * 取消发送的信息
     *
     * @param $id
     * @return bool
     */
    public function cancelPointToPointMessage($id): bool
    {
        $data = $this->getCancelPointData('CANCEL');

        return $this->insideMessageRepository->update($id, $data);
    }

    /**
     * 标记单条点对点业务消息为已读
     *
     * @param InsideMessage $message
     * @param $currentUser
     * @param array $data
     * @return bool
     */
    protected function readSingleMessage(InsideMessage $message, $currentUser, array $data): bool
    {
        if (0 !== strcmp($message->state, '0') || !$this->checkAuthorized($message, $currentUser)) {
            return false;
        }

        return $message->update($data);
    }

    /**
     * 标记系统推送信息为已读
     *
     * @param InsideMessage $message
     * @param $currentUser
     * @param array $data
     * @return bool
     */
    protected function readSystemMessage(InsideMessage $message, $currentUser, array $data): bool
    {
        $systemMessage = $this->insideMessageSystemRepository->findWhereFirst(['customer_id', '2'], ['message_id', $message->id]);

        if (
            empty($systemMessage) ||
            0 !== strcmp($systemMessage->state, '0') ||
            !$this->checkAuthorized($message, $currentUser, $systemMessage->customer_id)
        ) {
            return false;
        }

        return $systemMessage->update($data);
    }

    /**
     * 验证操作越权
     *
     * @param $message
     * @param null $customId
     * @return bool
     */
    protected function checkAuthorized($message, $currentUser, $customId = null): bool
    {
        $customId = $customId ?? $message->recipient_id;

        if (empty($currentUser)) {
            return false;
        }

        return 0 === strcmp($message->recipient_model, get_class($currentUser)) &&
            0 === strcmp($customId, $currentUser->id);
    }

    /**
     * 发件人和收件人信息整合
     *
     * @param int $id
     * @return array
     */
    protected function getSenderAndRecipientInfo(int $id): array
    {
        $result = [];
        $sendUser = optional(request())->user();
        $result['sender_id'] = $sendUser->id ?? '0';
        $result['sender_model'] = get_class($sendUser);
        $result['recipient_model'] = $this->getRecipientModel($result['sender_model']);
        $result['recipient_id'] = 0 === strcmp(static::MODEL_NAME['ADMIN'], $result['recipient_model']) ? '0' : $id;

        return $result;
    }


    /**
     * 获取收件人模型
     * 通过发件人模型，获得收件人模型
     *
     * @param $send
     * @return string
     */
    protected function getRecipientModel(string $send): string
    {
        $tmpArr = static::MODEL_NAME;
        $senderUser = array_keys($tmpArr, $send)[0];
        unset($tmpArr[$senderUser]);

        return array_first($tmpArr);
    }

    /**
     * 设置state的当前值
     *
     * @param $state
     * @return array
     */
    protected function getCancelPointData($state): array
    {
        return [
            'state' => static::MESSAGE_STATE[$state]
        ];
    }

    /**
     * 格式化系统信息批量写入
     *
     * @param array $ids
     * @param $messageId
     * @return bool
     */
    protected function createSystemMessageInfoForBatch(array $ids, $messageId, $num): array
    {
        $createdAt = date('Y-m-d H:i:s');

        return batch_merge($ids, $num, function ($start, $end) use ($ids, $messageId, $createdAt) {
            for ($i=$start; $i<$end; $i++) {
                $result[] = ['customer_id' => $ids[$i], 'message_id' => $messageId, 'created_at' => $createdAt];
            }

            return $this->insideMessageSystemRepository->createForBatch($result);
        });
    }

    /**
     * 报错站内信的基础信息
     *
     * @param array $data
     * @param $type
     * @return mixed
     */
    protected function saveBaseInsideMessageInfo(array $data, $type)
    {
        $sendAndRecInfo = $this->getSenderAndRecipientInfo($data['recipient_id']);
        $sendAndRecInfo['type'] = $type;
        $messageInfo = array_only($data, ['title', 'content', 'contact_way']);

        return $this->insideMessageContentRepository->saveContentAssociateInfo($messageInfo, $sendAndRecInfo);
    }
}